//
//  TCPAction.swift
//  swifttest1
//
//  Created by kologe on 2024/9/16.
//

import Foundation
import Network
import NetAction

let global_speed = -1
let SPEEDLINE = 5*1024*1024

class TCPClient{
    
    let connection: NWConnection
    var data:Data?
    var conspeed:FSIZE = 3*1024*1024 //本次连接的速度限制
    
    var lastData:Data = Data()
    
    //文件发送请求的字典
    var fileDic:Dictionary<FileId,FileTransMessage> = Dictionary()
    
    var taskArr:Array<BaseData> = Array()
    
    //设置属性代表要处理的工作的数量
    var taskNum:Int = 0
    lazy var taskCondition:NSCondition = NSCondition()
    var receiveBlokNum:Int = 0;
    
    var remoteIp:String = ""
    var conId:ConId = 0
    
    init(host:String,port:UInt16){
        remoteIp = host
        let nwEndpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(host), port: NWEndpoint.Port(rawValue: port)!)
        let nwParameters = NWParameters.tcp
        self.connection = NWConnection(to: nwEndpoint, using: nwParameters)
//        conId = Cal.hashObIden(iden: ObjectIdentifier(self.connection))
    }
    
    //只能执行一次，所以一旦new了一个对象就执行
    func start(){
        connection.stateUpdateHandler = { [self] newState in
            switch newState {
            case .setup:
                print("client Connection setup")
            case .waiting(let error):
                error_log("client Connection waiting with error: \(error.localizedDescription)")
            case .preparing:
                print("client Connection preparing")
            case .ready:
                print("client Connection established")
                self.sendMessage()
            case .failed(let error):
                error_log("client Connection failed with error: \(error.localizedDescription)")
            case .cancelled:
                print("client Connection cancelled")
                close()
            default:
                break
            }
        }
        connection.start(queue: .global())
        
    }
    
    func addTask( data: BaseData) {
        taskCondition.lock()
        taskNum += 1
        self.taskArr.append(data)
        taskCondition.signal()
        taskCondition.unlock()
    }
    
    func sendMessage(){
        /*
         遍历connectionDic 中以connection为key 的数据，然后发送数据。这个TCPClient对象可能被多个线程获取，所以要获得相应的线程安全性。并保证数据的发送的次数只有一次。
         增加一个属性，用来判断是否有新的数据要进行处理，这个属性也要保证线程安全性
         */
        while true{
            taskCondition.lock()
            //使用while 是防止虚假唤醒，由于各种原因，wait的线程可以在不被signal和brocastSignal的其他操作唤醒。
            while taskNum == 0 {
                taskCondition.wait()
            }
            for item in taskArr{
                if let data = BaseData.pkgData(obj: item){
                    sendData(data: data)
                }
            }
            taskNum = 0
            taskArr.removeAll()
            taskCondition.unlock()
            
            //保证永远都有一个block来接受数据
            if  self.receiveBlokNum <= 0{
                self.receiveData()
                self.receiveBlokNum = 1
            }
        }
    }
    

    
    func sendData(data:Data){
        connection.send(content: data, completion:.contentProcessed({ error in
            if let error = error{
                error_log("client failed to send data: \(error.localizedDescription)")
            }else{
                print("client Data sent succcessfully")
            }
        }))
    }
    
 
    
    
    func receiveData() {
        connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { data, _, isComplete, error in
            self.receiveBlokNum -= 1
            if let data = data, !data.isEmpty {
                var finData = data
                if self.lastData.count>0{
                    finData = self.lastData
                    self.lastData = Data()
                    finData.append(data)
                }
                TCPClientManager.splitDatawithConnection(data: finData) { ph, data, status in
                    if status{
                        self.lastData = data
                    }else{
                        //对数据进行处理
                        self.dealMessage(ph: ph!, data: data)
                    }
                    
                }
            }
            if isComplete {
                print("client Connection closed by server")
            } else if let error = error {
                let str = "client Failed to receive data: \(error.localizedDescription)"
                error_log(str)
            } else {
                self.receiveData()
                self.receiveBlokNum += 1
            }
        }
       
    }
    
    func dealMessage(ph:PackageHead,data:Data){
        switch ph.type{
        case FILE_MESSAGE_ANS:do {
            onreceiveFileMessageAns(ph: ph, data: data)
            break
        }
        case FILE_DATA_RECEIVE:do{
            
            break
        }
        case SPEED_REQ:do{
            onreceiveSpeedReq(data:data)
            break
        }
        case FILE_CANCEL:do{
            onreceiveFileCancel(data:data)
            break
        }
        case FILE_STOP:do{
            onreceiveFileStop(data:data)
            break
        }
        case FILE_RESUME:do{
            onreceiveFileResume(data:data)
            break
        }
        case RECONNECT:do{
            onreceiveFileReconnect(data:data)
            break
        }
            
        default: break
        }
    }
    
    func onreceiveSpeedReq(data:Data){
        
        if let message:SpeedMessage =  SpeedMessage.deCodeFromJson(jsonData: data){
            self.conspeed =  message.speed
        }
    }
    func onreceiveFileCancel(data:Data){
        if let message: FileCancel =  FileCancel.deCodeFromJson(jsonData: data){
            for index in fileUploadArr.array.indices{
                let ftm = fileUploadArr.array[index]
                if ftm.fileId == message.fileId{
                    ftm.state = INT32(DOWNLOADCANCEL)
                }
            }
            self.fileDic.removeValue(forKey: message.fileId)
        }
        
    }
    func onreceiveFileStop(data:Data){
        
        if let message: FileStop =  FileStop.deCodeFromJson(jsonData: data){
            for index in fileUploadArr.array.indices{
                let ftm = fileUploadArr.array[index]
                if ftm.fileId == message.fileId{
                    ftm.state = INT32(DOWNLOADPAUSE)
//                    fileClientPauseArr.add(obj: ftm)//保存暂停传输的数据的信息
                }
            }
        }
        
    }
    
    func onreceiveFileResume(data:Data){
        if let message: FileResume = FileResume.deCodeFromJson(jsonData: data){
            if !message.msg.isEmpty{
                var view = WarnText(size: NSMakeSize(450, 200));
                view.textStr = message.msg
                let id = Cal.hashOfString(str: message.msg)
                view.winId = id
              
                WindowDelegate.openWindow(view: view, title: "warn", size: NSMakeSize(450, 200), id: id)
//                for index in fileClientPauseArr.array.indices{
//                    let ftm = fileClientPauseArr.array[index]
//                    if ftm.fileId == message.fileId{
//                        ftm.state = DOWNLOADFAILED
//                    }
//                }
                for index in fileUploadArr.array.indices{
                    let ftm = fileUploadArr.array[index]
                    if ftm.fileId == message.fileId{
                        ftm.state = DOWNLOADFAILED
                    }
                }
               return
            }
//            for index in fileClientPauseArr.array.indices{
//                let ftm = fileClientPauseArr.array[index]
            if message.type == 1{
                for index in fileUploadArr.array.indices{
                    let ftm = fileUploadArr.array[index]
                    if ftm.fileId == message.fileId{
                        //直接发送发件发送请求信息，同时在flieDic中补全信息
                        ftm.downloadSize = message.size//重新设置大小
                        ftm.state = DOWNLOADING
                        let fdm = FileDataMsg( filePath: ftm.absolutePath!)
                        FileWriteManager.shared.addObjForKey(key: ftm.fileId, fdm: fdm)
                        sendFileData(ftm: ftm)
                        break
                    }
                }
            }else{
                let resume = FileResume(fileId:message.fileId,type: 1)
                addTask(data: resume)
                for index in fileUploadArr.array.indices{
                    let ftm = fileUploadArr.array[index]
                    if ftm.fileId == message.fileId{
                        //直接发送发件发送请求信息，同时在flieDic中补全信息
                        ftm.downloadSize = message.size//重新设置大小
                        ftm.state = DOWNLOADING
                        let fdm = FileDataMsg( filePath: ftm.absolutePath!)
                        FileWriteManager.shared.addObjForKey(key: ftm.fileId, fdm: fdm)
                        sendFileData(ftm: ftm)
                        break
                    }
                }
               
            }
        }
    }
    
    func onreceiveFileReconnect(data:Data){
        if let message: ReConnect = ReConnect.deCodeFromJson(jsonData: data){
            info_log("listen->client: reconnect with response.no need to response")
            let ip:String = getipFromEndpoint(addr: connection.endpoint.debugDescription)
            fileUploadArr.forEach{ ftm in
                if ftm.ip == ip{
                    self.fileDic[ftm.fileId] = ftm
                    ftm.conID = self.conId
                }
            }
            ConContainerMsg.shared.performTask(ip: ip, side: .CLIENT, conid: conId)
        }
    }
    
    func onreceiveFileMessageAns(ph:PackageHead,data:Data){
        //        let lenofph = PackageHead.headCount
        //        let msgData = data.subdata(in: Int(lenofph)..<Int(ph.dataCount - lenofph))
        let msgData = data
        
        if let message:FileMessage =  FileMessage.deCodeFromJson(jsonData: msgData){
            //1代表接受文件传送
            if 1 == message.state{
                //先加入fileUploadArr
                if let ftm = addFileUploadArr(fm:message){
                    // 发送文件
                    DispatchQueue.global(qos: .background).async{
                        self.sendFileData(ftm: ftm)
                    }
                }
            }else if 2 ==  message.state{
                //将其从现在的filedic中删除
                fileDic.removeValue(forKey: message.fileId)
            }
        }
        
    }
    //从fileDic中删除
    func delFromFileDic(fileId:FileId){
        fileDic.removeValue(forKey: fileId)
    }
    
    func addFileUploadArr(fm:FileMessage) -> FileTransMessage?{
        if let ftm = fileDic[fm.fileId]{
            ftm.ifTrans = 1 // 设置为已经发送
            fileUploadArr.add(obj: ftm)
            return ftm
        }
        return nil
    }
    
    func sendFileData(ftm:FileTransMessage){
        let fileHandle = FileHandle(forReadingAtPath: ftm.absolutePath!)
        defer {
            fileHandle?.closeFile()
        }
        var count:FSIZE = 0
        do{
            try fileHandle?.seek(toOffset: ftm.downloadSize)
            var keepRun = true;
            while keepRun{
                //设置终止发送
                if DOWNLOADCANCEL == ftm.state || DOWNLOADPAUSE == ftm.state
                {
                    return
                }
                //由于arc的自动清理功能所带来的缓存，导致while循环中的临时变量无法及时释放，因此需要自行释放此处的临时变量，但是会影响运行的速度
                //设置一个变量可以表示是否结束while循环
                try autoreleasepool {
                    guard let fileData = try fileHandle?.read(upToCount: Int(NORMALFILESIZE))
                    else{
                        DispatchQueue.main.async{
                            ftm.state = INT32(DOWNLOADFAILED)
                            self.delFromFileDic(fileId: ftm.fileId)
                        }
                        keepRun = false
                        return
                    }
                    if fileData.count == 0{
                        DispatchQueue.main.async{
                            ftm.state = INT32(DOWNLOADCOMPLETE)
                            self.delFromFileDic(fileId: ftm.fileId)
                        }
                        keepRun = false
                        return
                    }
                    let fdata = FileData(fileId: ftm.fileId,fileName: ftm.fileName!, fileData: fileData)
                    if let jsonData =  FileData.enCodeToJson(obj: fdata){
                        let pkgData = PackageHead.packageData(msgData: jsonData, type: FILE_DATA)
                        sendData(data: pkgData)
                    }
                    count += FSIZE(fileData.count)
                    if(count >= conspeed){
                        Thread.sleep(forTimeInterval:1)
                        count = 0
                    }
                    DispatchQueue.main.async{
                        ftm.downloadSize += FSIZE(fileData.count)
                    }
                    
                    print("sendFileData done,data size is : \(fileData.count)")
                    
    //                if(fileData.count < NORMALFILESIZE ){
    //                    //响应的fileuploadarr里面的数据要更新为发送完毕
    //                    ftm.state = INT32(DOWNLOADCOMPLETE)
    //                    delFromFileDic(fileId: ftm.fileId)
    //                    break
    //                }
                    if(ftm.downloadSize >= ftm.totalSize!){
                        DispatchQueue.main.async{
                            ftm.state = INT32(DOWNLOADCOMPLETE)
                            self.delFromFileDic(fileId: ftm.fileId)
                            if ftm.downloadSize > ftm.totalSize!{
                                error_log("sendFileData : downloadsize is larger than totalsize")
                            }
                        }
                        keepRun = false
                        return
                    }
                }
    
                
            }
        }catch{
            error_log("error is \(error.localizedDescription)")
        }
        info_log("send file data \(ftm.fileName ?? "nil") done")
        
    }
    
    
    
    
    func close(){
        fileDic.forEach { (key: FileId, value: FileTransMessage) in
            fileUploadArr.array.forEach({ ftm in
                if ftm.fileId == key{
                    if ftm.downloadSize < ftm.totalSize!,ftm.state == DOWNLOADING{
                        ftm.state = INT32(DOWNLOADPAUSE)
                    }
                }
            })
        }
        
        
        connection.cancel()
    }
    
    func dicconnect(connection:NWConnection){
        //发送结束连接的信息
        connection.send(content: nil, contentContext: .finalMessage, isComplete: true, completion: .contentProcessed { error in
            if let error = error {
                error_log("connection disconnect error: \(error.localizedDescription)")
            } else {
                print("connection disconnect")
            }
        })
    }

    
}
